Log In  
BBS > Lexaloffle Community Superblog
This is a combined feed of all Lexaloffle user blogs. For Lexaloffle-related news, see @zep's blog.

All | Following | PICO-8 | Voxatron | General | Off-site
[ :: Read More :: ]

tl;dr

In the pico8 console, run load #prof, then edit the last tab with some code you want to measure:

prof(function(x)
  local _=sqrt(x)   -- code to measure
end,function(x)
  local _=x^0.5     -- some other code to measure
end,{ locals={9} }) -- "locals" (optional) are passed in as args

Run the cart: it will tell you exactly how many cycles it takes to run each code snippet.


what is this?

The wiki is helpful to look up CPU costs for various bits of code, but I often prefer to directly compare two larger snippets of code against each other. (plus, the wiki can get out of date sometimes)

For the curious, here's how I'm able to calculate exact cycle counts
(essentially, I run the code many times and compare it against running nothing many times, using stat(1) and stat(2) for timing)

-- slightly simplified from the version in the cart
function profile_one(func)
  local n = 0x1000

  -- we want to type
  --   local m = 0x80_0000/n
  -- but 8๐˜ฎ๐˜ฉz is too large a number to handle in pico-8,
  -- so we do (0x80_0000>>16)/(n>>16) instead
  -- (n is always an integer, so n>>16 won't lose any bits)
  local m = 0x80/(n>>16)

  -- given three timestamps (pre-calibration, middle, post-measurement),
  --   calculate how many more ๐˜ค๐˜ฑ๐˜ถ cycles func() took compared to noop()
  -- derivation:
  --   ๐˜ต := ((t2-t1)-(t1-t0))/n (frames)
  --     this is the extra time for each func call, compared to noop
  --     this is measured in #-of-frames (at 30fps) -- it will be a small fraction for most ops
  --   ๐˜ง := 1/30 (seconds/frame)
  --     this is just the framerate that the tests run at, not the framerate of your game
  --     can get this programmatically with stat(8) if you really wanted to
  --   ๐˜ฎ := 256*256*128 = 8๐˜ฎ๐˜ฉz (cycles/second)
  --     (๐˜ฑ๐˜ช๐˜ค๐˜ฐ-8 runs at 8๐˜ฎ๐˜ฉz; see https://www.lexaloffle.com/dl/docs/pico-8_manual.html#CPU)
  --   cycles := ๐˜ต frames * ๐˜ง seconds/frame * ๐˜ฎ cycles/second
  -- optimization / working around pico-8's fixed point numbers:
  --   ๐˜ต2 := ๐˜ต*n = (t2-t1)-(t1-t0)
  --   ๐˜ฎ2 := ๐˜ฎ/n := m (e.g. when n is 0x1000, m is 0x800)
  --   cycles := ๐˜ต2*๐˜ฎ2*๐˜ง
  local function cycles(t0,t1,t2) return ((t2-t1)-(t1-t0))*m/30 end

  local noop=function() end -- this must be local, because func is local
  flip()
  local atot,asys=stat(1),stat(2)
  for i=1,n do noop() end -- calibrate
  local btot,bsys=stat(1),stat(2)
  for i=1,n do func() end -- measure
  local ctot,csys=stat(1),stat(2)

  -- gather results
  local tot=cycles(atot,btot,ctot)
  local sys=cycles(asys,bsys,csys)
  return {
    lua=tot-sys,
    sys=sys,
    total=tot,
  }
end

how do I use it?

Here's an older demo to wow you:

Cart #cyclecounter-2 | 2022-01-16 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
15

This is neat but impractical; for everyday usage, you'll want to load #prof and edit the last tab.

The cart comes with detailed instructions, reproduced here for your convenience:

-----------------------
-- โ˜… usage guide โ˜… --
-----------------------

์›ƒ: i have two code snippets;
    which one is faster?

๐Ÿฑ: edit the last tab with your
    snippets, then run the cart.
    it will tell you precisely
    how much cpu it takes to
    run each snippet.

    the results are also copied
    to your clipboard.

์›ƒ: what do the numbers mean?

๐Ÿฑ: the cpu cost is reported
    as lua and system cycle
    counts. look up stat(1)
    and stat(2) for more info.

    if you're not sure, just
    look at the first number.
    lower is faster (better)

์›ƒ: why "{locals={9}}"
    in the example?

๐Ÿฑ: accessing local variables
    is faster than global vars.

    so if your test involves
    local variables, simulate
    this by passing them in:

      prof(function(a)
        sqrt(a)
      end,{ locals={9} })

    /!\     /!\     /!\     /!\
    local values from outside
    the current scope are also
    slower to access! example:

      global = 4
      local outer = 4
      prof(function(x)
        local _ = x --fast
      end,function(x)
        local _ = outer --slow
      end,function(x)
        local _ = global --slow
      end,{ locals={4} })
    /!\     /!\     /!\     /!\

์›ƒ: can i do "prof(myfunc)"?

๐Ÿฑ: no, this sometimes gives
    wrong results! always use
    inline functions:

      prof(function()
        --code for myfunc here
      end)

    as an example, "prof(sin)"
    reports "-2" -- wrong! but
    "prof(function()sin()end)"
    correctly reports "4"

    (see the technical notes at
    the start of the next tab
    for a brief explanation.
    technically, "prof(myfunc)"
    will work if myfunc was made
    by the user, but you will
    risk confusing yourself)

There are also instructions included on two alternate ways you can profile your code, without using prof:

---------------
 โ˜… method 2 โ˜…
---------------

this cart is based on
code by samhocevar:
https://www.lexaloffle.com/bbs/?pid=60198#p

if you do this method, be very
careful with local/global vars.
it's very easy to accidentally
measure the wrong thing.

here's an example of how to
measure cycles (ignoring this
cart and using the old method)

  function _init()
    local a=11.2 -- locals

    local n=1024
    flip()
    local tot1,sys1=stat(1),stat(2)
    for i=1,n do   end --calibrate
    local tot2,sys2=stat(1),stat(2)
    for i=1,n do local _=sqrt(a) end --measure
    local tot3,sys3=stat(1),stat(2)

    function cyc(t0,t1,t2) return ((t2-t1)-(t1-t0))*128/n*256/stat(8)*256 end
    local lua = cyc(tot1-sys1,tot2-sys2,tot3-sys3)
    local sys = cyc(sys1,sys2,sys3)
    print(lua.."+"..sys.."="..(lua+sys).." (lua+sys)")
  end

run this once, see the results,
then change the "measure" line
to some other code you want
to measure.

note: wrapping the code inside
"_init()" is required, otherwise
builtin functions like "sin"
will be measured wrong.
(the reason is explained at
the start of the next tab)

---------------
 โ˜… method 3 โ˜…
---------------

another way to measure cpu cost
is to run something like this:

  function _draw()
    cls(1)
    local x=9
    for i=1,1000 do
      local a=sqrt(x) --snippet1
  --    local b=x^0.5 --snippet2
    end
  end

while running, press ctrl-p to
see the performance monitor.
the middle number shows how much
of cpu is being used, as a
fraction. (0.60 = 60% used)

now, change the comments on the
two code snippets inside _draw()
and re-run. compare the new
result with the old to determine
which snippet is faster.

note: every loop iteration costs
an additional 2 cycles, so the
ratio of the two fractions will
not match the ratio of the 
execution time of the snippets.
but this method can quickly tell
you which snippet is faster.

various results

Here are some speed comparisons I found interesting. Some of these may be out of date now, but they were interesting:

poke4 v. memcopy

prof(function() memcpy(0,0x200,64) end,       -- 71 (7 lua, 64 sys)
     function() poke4(0,peek4(0x200,16)) end) -- 67 (7 lua, 60 sys)

Copying 64 bytes of memory is very slightly faster if you use poke4 instead of memcpy -- interesting!
(iirc this is true for other data sizes... find out for yourself for sure by downloading and running the cart!)

edit: this has changed in 0.2.4b! the memcpy in this example now takes 39 cycles

constant folding

I thought lua code was not optimized by the lua compiler/JIT at all, but it turns out there are a few specific optimizations it will do.

prof(function() return 2+2 end,
     function() return 2+2+2+2+2+2+2+2 end)

These functions both take a single cycle! That long addition gets optimized by lua, apparently. @luchak found these explanations:

https://stackoverflow.com/questions/33991369/does-the-lua-compiler-optimize-local-vars/33995520
> Since Lua often compiles source code into byte code on the fly, it is designed to be a fast single-pass compiler. It does do some constant folding

A No Frills Introduction to Lua 5.1 VM Instructions (book)
> As of Lua 5.1, the parser and code generator can perform limited constant expression folding or evaluation. Constant folding only works for binary arithmetic operators and the unary minus operator (UNM, which will be covered next.) There is no equivalent optimization for relational, boolean or string operators.

constant folding...?

One further test case:

prof(function() local a=2  return 2+2+2+2+2+2+2+a end, --2
     function() local a=2  return a+2+2+2+2+2+2+2 end) --8

These cost different amounts! Constant-folding only seems to work at the start of expressions. (This is all highly impractical code anyway, but it's fun to dig in and figure out this sort of thing)

credits

Cart by pancelor.

Thanks to @samhocevar for the initial snippet that I used as a basis for this profiler!

Thanks to @freds72 and @luchak for discussing an earlier version of this with me!

Thanks to thisismypassword for updating the wiki's CPU page!

changelog

v1.4

  • redo explanations
  • more thorough explanation of pitfalls of alternate methods
    • why measuring sin() at top-level is no good
    • why function _draw() for i=1,1000 do ... end end can be misleading

v1.3

  • simpler BBS post, friendlier cart instructions

v1.2

  • rewrite; recommend using load #prof instead now

v1.1

  • added: press X to copy to clipboard
  • added: can pass args; e.g. profile("lerp", lerp, {args={1,4,0.3}})

v1.0

  • intial release
P#104795 2022-01-11 03:31 ( Edited 2024-03-11 09:38)
[ :: Read More :: ]

Cart #sidijafizo-1 | 2022-01-11 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5


Hello and thank you for taking you time to look at this post. Please try out my first little game. Be gentle lol. it's just a day at the park with my dog Zelda playing fetch.

P#104770 2022-01-10 20:59 ( Edited 2022-01-11 05:15)
[ :: Read More :: ]

Cart #map_col142-0 | 2022-01-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
8


This is an open-source cartridge/engine with map collision, player movement, and map scrolling. (206 tokens in total)

P#104766 2022-01-10 20:40 ( Edited 2022-01-10 21:20)
[ :: Read More :: ]

Cart #duwifisora-0 | 2022-01-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
3

P#104761 2022-01-10 19:18
[ :: Read More :: ]

Cart #tipapeks-0 | 2022-01-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1

P#104760 2022-01-10 19:12
[ :: Read More :: ]

One post, two topics -- but I figured they are mildly related here since it's really up to zep in both cases :D

I know it's a long shot, but is there any chance of getting a port of PICO-8 to MIPS? Specifically, I want to be able to use my Abernic RG280V to play PICO-8 games on the Adam image (https://www.youtube.com/watch?v=kdIdiQ-dO_Y).

Second topic. Is official Apple Silicon (aka ARM64) support on the roadmap for the Mac version? I know the Intel version runs just fine under Rosetta, but there is still downsides to running Intel code on these machines, like additional memory and battery usage. Plus it future proofs things a little for the eventual (still years away) removal of Rosetta by Apple in a future OS update.

P#104758 2022-01-10 19:06
[ :: Read More :: ]

Cart #midarodafe-0 | 2022-01-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#104756 2022-01-10 18:52
[ :: Read More :: ]

Cart #aerialrave1_10_22-0 | 2022-01-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5

Second work in progress release based on user feedback.

Score, style meter, deathscreen with results and office practice room added.
Killfloor rises much slower, rising faster based on your level
Filling boost is made consistent, takes about 5 kills (fountains count as half)

Style rank adds power to your boost! The higher your rank the higher you fly!

[[z,x, and arrow keys to control
double jump recharges with time
double tap and hold direction to stinger attack
You can walljump (though not for much height)
hold up or hold down to do different attacks. Up attacking a stunned enemy gives you height!
Hold x while doing down attack to keep it active. Doing this on a stunned enemy stops this from canceling.
if you do one of the hold button attacks on a stunned enemy (when they flash) it does something different
Normal slashes are best for causing stuns
hold z to use super jump when red bar is filled
Fill the bar with kills (The fountains count!)]]

P#104727 2022-01-10 06:53 ( Edited 2022-01-10 06:54)
[ :: Read More :: ]

Cart #to_infinity-0 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
4

TO LOAD THIS CART in immediate mode, type: load #to_infinity

Written using 5 Pico-8 commands, see code.

Back when i was writing S2 (scenario rpgmaker 2), I wanted to have that cool neat zoom effect like you get on the SNES and more modern RPGs when you enter combat, you know, where the screen would blur and rotate and zoom forward.

At the time I was working in GFA for windows 3.1 and was told it could not be done, that it would require me to grab every pixel on the screen, calculate their point outward, then redraw them.

And yeah the first time I attempted that it was terribly slow and not even that pretty.

But then i got to thinking about blitter objects. You could copy from one blitter plane to another and could even stretch them - so why not make use of a single stretched plane to get that zoom effect I wanted ?

While not perfect it is still pretty impressive for what it can do using zero variables - and since we now have extended memory to work with in Pico-8, it's overdue that I share with you the method on how I did it so quickly and easily those many years ago from today by making use of that extended memory.

Have your screen in whatever state you want it in, then call my toinfinity() routine to have it in one step zoom the screen outward with distortion.

And that's it! No need to set-up anything or any arrays. In use it does not affect sprites, mapper, or sound, even if changed already in the program.

It =does= use up memory locations E000-FFFF temporarily, but that's about it.

If you like it please let me know !

THANKS !

P#104689 2022-01-09 21:38 ( Edited 2023-01-28 18:16)
[ :: Read More :: ]


c-base - drink all the mate

my very first and very simple club-mate drinking game :D

footage

story

you are a nerd that needs all the club-mate they can get
every step drains your energy but club-mate can refill it
when you finished all the bottles you win
if you have no energy left and also no mate you loose

controls

you start the game with X

just walk over the club-mate bottles to drink them
and replenish your energy

why?

i just wanted to draw the c-base hackerspace logo in pico-8.
then i thought, ok what game could i write?
so i thought of c-base and whats there a lot. It's nerds and mate.
so story found, you are a nerd in a nice dark hoodie on the search for
all the club-mate in c-base.

resources used

P#104687 2022-01-09 21:10 ( Edited 2022-01-10 20:33)
[ :: Read More :: ]

A basic example of how to do alpha masking with multiple masks (or light sources) that can intersect each other.

Cart #alphamask-0 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1

P#104683 2022-01-09 20:42
[ :: Read More :: ]

I was wondering its fair making the developer kit paid but why splore? Its actually a better version of the cartridge list on browser so why make it exclusive to the dev kit?

P#104679 2022-01-09 19:51
[ :: Read More :: ]

Hello everyone,

I am working on some test code for a game idea and it involved picking up one object at a time. Now my code functions properly for the most part but for some reason the p.detect won't trigger for the key but it will for the gun. And the key can't be placed on top of the gun (correct) but the gun can be placed on top of the key (not meant to happen).

If someone wouldn't mind taking a look for me, most of the code I'm currently working with in is the objects functions and the player functions.

The boolean values are:

p.detect
p.held
key.detect
key.held
gun.detect
gun.held

Cart #yonezijiko-0 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#104678 2022-01-09 19:43
[ :: Read More :: ]

Cart #jabbergranny-0 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
2

Made in three days for the "one-game a week" course Playing with Games, at the Cologne Game Lab's Master of Art in Game Development and Research.

Astrid Busch & Marcel Pace

P#104674 2022-01-09 19:27
[ :: Read More :: ]

Put all requests for games, mods (and genres) here:

format games as
name
:name|author|id|

format mods as:
โ–’cart name|creator|#id|

IMPORTANT:
a genre is needed to add it to the program

ALL future [carts,mods,genres] added will be a result of comments
I want to put in what you want to play!

(mods for already added games work as well as new games)

game is here (again):
https://www.lexaloffle.com/bbs/?tid=46049

once added, please delete your comment to remove clutter and make it easier for me to know what I still need to add

P#104666 2022-01-09 18:40 ( Edited 2022-01-14 12:51)
[ :: Read More :: ]

Cart #horse_race-1 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
2

Horse_Race 0.2

Just testing Pico-8 development.

Simple horse race game. Nothing to control just guess which horse will win the race.

P#104654 2022-01-09 14:58 ( Edited 2022-01-09 15:12)
[ :: Read More :: ]

Game Here

https://www.lexaloffle.com/bbs/?tid=46377

P#104640 2022-01-09 07:23 ( Edited 2022-01-31 06:55)
[ :: Read More :: ]

Cart #aerialravewip1_9_22-0 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

z,x, and arrow keys to control
double jump recharges with time
double tap and hold direction to stinger attack
You can walljump (though not for much height)
hold up or hold down to do different attacks. Up attacking a stunned enemy gives you height!
Hold x while doing down attack to keep it active. Doing this on a stunned enemy stops this from canceling.
if you do one of the hold button attacks on a stunned enemy (when they flash) it does something different
Normal slashes are best for causing stuns
hold z to use super jump when red bar is filled
Fill the bar with kills (The fountains count! Downslash kills give the most bar!)
please lmk what you think any issues etc!

P#104623 2022-01-09 06:28 ( Edited 2022-01-09 08:22)
[ :: Read More :: ]

Cart #shmupengineprototype01-0 | 2022-01-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
3

Hey all,

I've built a shoot-em-up game engine inspired by Tyrian, a classic of the genre released for MS-DOS in 1995. I'm stuck for now on my lack of artistic skill for graphics, art, a title screen, a menu...

I'm a fan of declarative programming, so I've gone pretty far down that path. The base implementations of ships, guns, and bullets handle clamping to the screen space, autoscrolling, acceleration + momentum + drag, damage, regenerating shields, limited power from a generator (which your weapons and shields both draw from), moving in straight lines (for bullets), limited ammo (optional), and poorly-animated explosions.

I've implemented powerups for recovering health and switching weapons. Weapons can have limited ammo.

I've implemented a general engine for progressing through a level. A level is a table from "distance" to 0-argument functions, which will typically spawn enemies (or powerups, or start events). A level can be "frozen", which stops it from incrementing distance or executing new level events, allowing for boss fights and the like.

Collisions between player bullets and enemy ships are optimized by breaking the screen into a grid of regions; a ship only checks for collisions with bullets in the regions that its own hitbox intersects with. (Ship-ship collisions and enemy-bullet on player ship collisions are brute force because there is usually only one player ship and the overhead of calculating the grid is pure dead weight.)

I've got a bunch of documentation in tab 8 - I hope it's enough for folks to develop their own ships, weapons, and levels with! In retrospect, I think I need to document that ship and event lifespan is managed as follows: when an event, move(), hitship(), or hitbullet() call returns true (or true-ish), it's dropped.

The example level is just a sandbox I was testing stuff in. The graphics are very distant from anything I'd really want to release. But the engine is there, and I think it's cool, and I'd be really excited to see anyone else using it to design a shmup of their own. It's a Tyrian-inspired shmup engine that doesn't have quite enough pieces to be a shmup kit, but I hope to get there.

P#104614 2022-01-09 05:09 ( Edited 2022-01-09 05:16)
[ :: Read More :: ]

Cart #nusudeteju-3 | 2022-01-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
6

First milestone for a battleship game. Everything is functioning, but lots left to do.

Currently there's still no AI, sound, artwork... though I've already learned a lot with this project. I figured out how to set up an external editor, do some basic logging, and how pallet changing works.

Next up is the AI, which is definitely an area I'm more comfortable with, though I'm sure I'll find some challenges anyways!


Updates:

  • Added a simple AI
P#104590 2022-01-09 02:07 ( Edited 2022-01-23 03:48)
View Older Posts